home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Mac Game Programming Gurus / TricksOfTheMacGameProgrammingGurus.iso / More Source / C⁄C++ / Xconq 7.0d37 / source / mac / macwins.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-05-02  |  44.3 KB  |  1,636 lines  |  [TEXT/KAHL]

  1. /* Handling of assorted minor windows for the Mac interface to Xconq.
  2.    Copyright (C) 1992, 1993, 1994, 1995 Stanley T. Shebs.
  3.  
  4. Xconq is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2, or (at your option)
  7. any later version.  See the file COPYING.  */
  8.  
  9. #include "conq.h"
  10. #include "macconq.h"
  11. extern int do_key_down_construction(int);
  12.  
  13. /* Globals for the game window. */
  14.  
  15. WindowPtr gamewin = nil;
  16.  
  17. int gamewinw = 200;
  18. int gamedatehgt = 18;
  19. int gameclockoffset;
  20. int gameclockhgt = 15;
  21. int gamenoteoffset;
  22. int gamenotehgt = 15;
  23. int gametophgt;
  24.  
  25. int gamesidehgt = 24;
  26. int gamesideclockhgt = 15;
  27. int gamesideclockoffset;
  28. int gamesidescorehgt = 15;
  29. int gamesidescoreoffset;
  30.  
  31. Handle aisicnhandle = nil;
  32. Handle facesicnhandle[3];
  33.  
  34. char *game_progress_str = "";
  35.  
  36. time_t lastnow;
  37.  
  38. /* Globals for the construction window. */
  39.  
  40. WindowPtr constructionwin = nil;
  41.  
  42. ListHandle construction_unit_list = nil;
  43. ListHandle constructiontypelist = nil;
  44.  
  45. int constructmargin = 5;
  46. int constructtop = 32;
  47.  
  48. /* This is the vector of units that can do construction or research. */
  49.  
  50. UnitVector *possibleunits = NULL;
  51.  
  52. int numposstypes;
  53. int *possibletypes = NULL;
  54.  
  55. int currunlength = 99;
  56.  
  57. ControlHandle constructbutton;
  58. ControlHandle researchbutton;
  59.  
  60. TEHandle run_length_text = nil;
  61.  
  62. Rect runlengthrect, unitlistrect, typelistrect;
  63.  
  64. /* Globals for unit closeup windows. */
  65.  
  66. int lastunitcloseuph = -1, lastunitcloseupv = -1;
  67.  
  68. /* Globals for the history window. */
  69.  
  70. WindowPtr historywin = nil;
  71.  
  72. HistEvent **histcontents = NULL;
  73.  
  74. int numhistcontents = 0;
  75.  
  76. ControlHandle histvscrollbar;
  77.  
  78. int history_line_spacing = 15; /* (should derive from font) */
  79.  
  80. int history_top_line_height = 25;
  81.  
  82. HistEvent *firstvisevt = NULL;
  83.  
  84. HistEvent *secondvisevt = NULL;
  85.  
  86. int numvishistlines;
  87.  
  88. int total_history_lines = 0;
  89.  
  90. /* The game progress window. */
  91.  
  92. /* Create the game progress window. */
  93.  
  94. void
  95. create_game_window()
  96. {
  97.     extern int numscores;
  98.  
  99.     /* Create the window, color if possible, since emblems may be in color. */
  100.     if (hasColorQD) {    
  101.         gamewin = GetNewCWindow(wGame, NULL, (WindowPtr) -1L);
  102.     } else {
  103.         gamewin = GetNewWindow(wGame, NULL, (WindowPtr) -1L);
  104.     }
  105.     gametophgt = gamedatehgt;
  106.     if (g_rt_per_turn() > 0 || g_rt_for_game() > 0) {
  107.         gameclockoffset = gametophgt;
  108.         gametophgt += gameclockhgt;
  109.     }
  110.     gamenoteoffset = gametophgt;
  111.     gametophgt += gamenotehgt;
  112.     /* Add some space if sides have a per-turn and/or per-game clock. */
  113.     if (g_rt_per_side() > 0) {
  114.         gamesideclockoffset = gamesidehgt;
  115.         gamesidehgt += gamesideclockhgt;
  116.     }
  117.     /* Add additional space for each two scorekeepers. */
  118.     if (keeping_score()) {
  119.         gamesidescoreoffset = gamesidehgt;
  120.         gamesidehgt += gamesidescorehgt * ((numscorekeepers + 1) / 2);
  121.     }
  122.     /* This is not growable, so we have to ensure it's big enough to start with. */
  123.     SizeWindow(gamewin, gamewinw, gametophgt + numsides * gamesidehgt, 1);
  124.     if (first_windows) {
  125.         GDHandle main_screen = GetMainDevice();
  126.         int main_screen_width, main_screen_height;
  127.         Rect gdrect = (*main_screen)->gdRect;
  128.  
  129.         main_screen_width = gdrect.right - gdrect.left;
  130.         main_screen_height = gdrect.bottom - gdrect.top;
  131.         MoveWindow(gamewin,
  132.                    main_screen_width - gamewinw - 3,
  133.                    40,
  134.                    FALSE);
  135.     }
  136.     /* Get handles to useful sicns. */
  137.     aisicnhandle = GetNamedResource('SICN', "\pmplayer");
  138.     facesicnhandle[0] = GetNamedResource('SICN', "\phostile");
  139.     facesicnhandle[1] = GetNamedResource('SICN', "\pneutral");
  140.     facesicnhandle[2] = GetNamedResource('SICN', "\pfriendly");
  141. }
  142.  
  143. void
  144. draw_game()
  145. {
  146.     Side *side2;
  147.     GrafPtr oldport;
  148.  
  149.     if (gamewin == nil)
  150.       return;
  151.     GetPort(&oldport);
  152.     SetPort(gamewin);
  153.     draw_game_date();
  154.     draw_game_progress();
  155.     /* Draw a solid separating line between date info and side list. */
  156.     MoveTo(0, gametophgt);
  157.     Line(gamewinw, 0);
  158.     for_all_sides(side2)
  159.       draw_game_side(side2);
  160.     SetPort(oldport);
  161. }
  162.  
  163. /* Display the current time and date and any realtime countdowns. */
  164.  
  165. void
  166. draw_game_date()
  167. {
  168.     Rect tmprect;
  169.  
  170.     SetRect(&tmprect, 0, 0, gamewinw, gamedatehgt - 1);
  171.     EraseRect(&tmprect);
  172.     MoveTo(tmprect.left + 10, tmprect.top + 12);
  173.     TextFace(bold);
  174.     DrawString((unsigned char *) curdatestr);
  175.     TextFace(0);
  176.     /* (should draw season name here somewhere?) */
  177.     draw_game_clocks();
  178.     if (endofgame) {
  179.         gray_out_rect(&tmprect);
  180.     }
  181. #ifdef DEBUGGING
  182.     /* Indicate the state of all the debug flags. */
  183.     if (Debug || DebugM || DebugG) {
  184.         sprintf(spbuf, "%c%c%c",
  185.                 (Debug ? 'D' : ' '), (DebugM ? 'M' : ' '), (DebugG ? 'G' : ' '));
  186.         MoveTo(tmprect.right - 30, tmprect.top + 12);
  187.         DrawText(spbuf, 0, strlen(spbuf));
  188.     }
  189. #endif /* DEBUGGING */
  190. }
  191.  
  192. void
  193. draw_game_clocks()
  194. {
  195.     int elapsed, s2, sy;
  196.     time_t now;
  197.     Rect tmprect;
  198.     Side *side2;
  199.  
  200.     /* Draw per-turn and per-game time limits that for the game as a whole. */
  201.     SetRect(&tmprect, 0, gamedatehgt, gamewinw / 2, gamedatehgt + gameclockhgt - 1);
  202.     if (g_rt_per_turn() > 0) {
  203.         time(&now);
  204.         elapsed = (int) difftime(now, turn_play_start_in_real_time);
  205.         time_desc(spbuf, g_rt_per_turn() - elapsed, g_rt_per_turn());
  206.         EraseRect(&tmprect);
  207.         MoveTo(tmprect.left + 20, tmprect.top + 10);
  208.         DrawText(spbuf, 0, strlen(spbuf));
  209.         lastnow = now;
  210.     }
  211.     OffsetRect(&tmprect, 100, 0);
  212.     if (g_rt_for_game() > 0) {
  213.         time(&now);
  214.         elapsed = (int) difftime(now, game_start_in_real_time);
  215.         time_desc(spbuf, g_rt_for_game() - elapsed, g_rt_for_game());
  216.         EraseRect(&tmprect);
  217.         MoveTo(tmprect.left + 10, tmprect.top + 10);
  218.         DrawText(spbuf, 0, strlen(spbuf));
  219.         lastnow = now;
  220.     }
  221.     /* Draw per-side clocks if any limits defined. */
  222.     if (g_rt_per_side() > 0) {
  223.         for_all_sides(side2) {
  224.             if (side2->ingame) {
  225.                 s2 = side_number(side2);
  226.                 sy = gametophgt + (s2 - 1) * gamesidehgt + gamesideclockoffset;
  227.                 elapsed = 0;
  228.                 if (!side2->finishedturn)
  229.                   elapsed = (int) difftime(now, turn_play_start_in_real_time); /* should be side start */
  230.                 time_desc(spbuf, g_rt_per_side() - side2->totaltimeused - elapsed, g_rt_per_side());
  231.                 SetRect(&tmprect, 0, sy, gamewinw, sy + gamesideclockhgt - 1);
  232.                 EraseRect(&tmprect);
  233.                 MoveTo(tmprect.left + 20, tmprect.top + 10);
  234.                 DrawText(spbuf, 0, strlen(spbuf));
  235.                 /* (should draw per-turn side usage) */
  236.             }
  237.         }
  238.     }
  239. }
  240.  
  241. void
  242. draw_game_progress()
  243. {
  244.     Rect tmprect;
  245.  
  246.     SetRect(&tmprect, 0, gamenoteoffset, gamewinw, gamenoteoffset + gamenotehgt - 1);
  247.     EraseRect(&tmprect);
  248.     MoveTo(1, gamenoteoffset + 12);
  249.     DrawText(game_progress_str, 0, strlen(game_progress_str));
  250. }
  251.  
  252. /* Draw info about a given side. */
  253.  
  254. void
  255. draw_game_side(Side *side2)
  256. {
  257.     int s2 = side_number(side2);
  258.     int sx = 20, sy = gametophgt + (s2 - 1) * gamesidehgt;
  259.  
  260.     draw_side_emblem(gamewin, 2, sy + 4, 16, 16, s2, shadow_emblem);
  261.     strcpy(spbuf, short_side_title(side2));
  262.     MoveTo(sx, sy + 12);
  263.     /* Put the name of our side in boldface. */
  264.     TextFace((side2 == dside ? bold : 0));
  265.     DrawText(spbuf, 0, strlen(spbuf));
  266.     TextFace(0);
  267.     if (side_has_ai(side2) && side2->ingame) {
  268.         /* Show that the side is run by an AI. */
  269.         plot_sicn(gamewin, 182, sy + 2, aisicnhandle, 0, TRUE, srcOr);
  270.     }
  271.     if (side2 != dside
  272.         && side2->ingame
  273.         && (side_has_ai(side2) || side_has_display(side2))) {
  274.         /* Indicate attitude of other side. */
  275.         plot_sicn(gamewin, 164, sy + 2,
  276.             facesicnhandle[feeling_towards(side2, dside)], 0, TRUE, srcOr);
  277.     }
  278.     draw_side_status(side2);
  279.     /* Draw a separating line. */
  280.     PenPat(QDPat(gray));
  281.     MoveTo(0, sy + gamesidehgt);
  282.     Line(gamewinw, 0);
  283.     PenNormal();
  284. }
  285.  
  286. /* (should make this more generic) */
  287.  
  288. int
  289. feeling_towards(Side *side, Side *side2)
  290. {
  291.     if (trusted_side(side2, side)) {
  292.         return 2;
  293.     } else if (side_has_ai(side) && should_try_to_win(side)) {
  294.         return 0;
  295.     } else {
  296.         return 1;
  297.     }
  298. }
  299.  
  300. /* Draw the current details about a side. */
  301.  
  302. void
  303. draw_side_status(Side *side2)
  304. {
  305.     int sx, sy = gametophgt + (side_number(side2) - 1) * gamesidehgt, i;
  306.     int totacp, resvacp, acpleft, percentleft, percentresv;
  307.     Rect siderect, tmprect, progressrect;
  308.     Scorekeeper *sk;
  309.     extern int curpriority;
  310.  
  311.     SetRect(&siderect, 0, sy + 1, gamewinw, sy + gamesidehgt);
  312.     if (!side2->ingame || endofgame) {
  313.         gray_out_rect(&siderect);
  314.         if (side_won(side2)) {
  315.             /* (should) Indicate that this side has won. */
  316.             /* draw like a trophy or flourishes or some such?) */
  317.         } else if (side_lost(side2)) {
  318.             /* Draw a (solid) line crossing out the loser.  Simple and obvious. */
  319.             MoveTo(1, sy + 8);
  320.             Line(gamewin->portRect.right - 3, 0);
  321.         }
  322.     } else {
  323.         /* Set up and clear the area where we show progress. */
  324.         SetRect(&progressrect, 20, sy + 12 + 4, 20 + 100, sy + 12 + 4 + 7);
  325.         EraseRect(&progressrect);
  326.         if (!g_use_side_priority() || curpriority == side2->priority) {
  327.             /* Show the current acp totals/progress of the side. */
  328.             /* This is not quite the security hole it might seem,
  329.                you don't get much advantage out of seeing how far along each side is,
  330.                and it gives you a feel for how the turn is progressing. */
  331.             totacp = side_initacp(side2);
  332.             if (totacp > 0) {
  333.                 FrameRect(&progressrect);
  334.                 acpleft = side_acp(side2);
  335.                 resvacp = side_acp_reserved(side2);
  336.                 if (totacp > 0) {
  337.                     percentleft = (100 * acpleft) / totacp;
  338.                     percentleft = max(0, min(99, percentleft));
  339.                     percentresv = (100 * resvacp) / totacp;
  340.                     percentresv = max(0, min(99, percentresv));
  341.                 } else {
  342.                     percentleft = percentresv = 0;
  343.                 }
  344.                 if (percentleft > 0) {
  345.                     tmprect = progressrect;
  346.                     InsetRect(&tmprect, 1, 1);
  347.                     tmprect.right = tmprect.left + percentleft;
  348.                     FillRect(&tmprect, QDPat(black));
  349.                 }
  350.                 if (percentresv > 0) {
  351.                     PenPat(QDPat(gray));
  352.                     MoveTo(progressrect.left + 1 + percentresv, progressrect.top);
  353.                     Line(0, 6);
  354.                     PenNormal();
  355.                     /* (or could draw a grayish area??) */
  356.                 }
  357.             } else {
  358.                 /* Draw a gray frame to indicate that this side has no units
  359.                    that can actually do anything. */
  360.                 PenPat(QDPat(gray));
  361.                 FrameRect(&progressrect);
  362.                 PenNormal();
  363.             }
  364.         }
  365.         /* (should this be a generic kernel test?) */
  366.         if (side2->finishedturn || !(side_has_ai(side2) || side_has_display(side2))) {
  367.             tmprect = progressrect;
  368.             InsetRect(&tmprect, 1, 1);
  369.             gray_out_rect(&tmprect);
  370.         }
  371.     }
  372.     if (keeping_score()) {
  373.         siderect.top += gamesidescoreoffset;
  374.         siderect.bottom = siderect.top + gamesidescorehgt - 1;
  375.         i = 0;
  376.         for_all_scorekeepers(sk) {
  377.             if (symbolp(sk->body)
  378.                 && match_keyword(sk->body, K_LAST_SIDE_WINS)) {
  379.                 int points = 0;
  380.                 Unit *unit;
  381.  
  382.                 for_all_side_units(side2, unit) {
  383.                     if (in_play(unit) && completed(unit)) {
  384.                         points += unit_point_value(unit);
  385.                     }
  386.                 }
  387.                 sprintf(spbuf, "Point Value: %d", points);
  388.             } else {
  389.                 /* Compose the generic scorekeeper status display. */
  390.                 if (sk->title != NULL) {
  391.                     sprintf(spbuf, "%s", sk->title);
  392.                 } else {
  393.                     sprintf(spbuf, "SK #%d", sk->id);
  394.                 }
  395.                 if (sk->scorenum >= 0) {
  396.                     tprintf(spbuf, ": %d", side2->scores[sk->scorenum]);
  397.                 }
  398.             }
  399.             if ((i & 1) == 0)
  400.               EraseRect(&siderect);
  401.             sx = (((i & 1) == 1) ? gamewinw / 2 : 0);
  402.             /* Draw the scorekeeper's status. */
  403.             MoveTo(sx + 10, siderect.top + 10);
  404.             DrawText(spbuf, 0, strlen(spbuf));
  405.             ++i;
  406.             if ((i & 1) == 0)
  407.               OffsetRect(&siderect, 0, gamesidescorehgt);
  408.         }
  409.     }
  410. }
  411.  
  412. void
  413. do_mouse_down_game(Point mouse, int mods)
  414. {
  415.     beep();
  416. }
  417.  
  418. /* The construction planning window. */
  419.  
  420. void
  421. create_construction_window()
  422. {
  423.     int done = FALSE;
  424.     Point cellsize;
  425.     Rect listrect, tmprect;
  426.  
  427.     if (hasColorQD) {
  428.         constructionwin = GetNewCWindow(wConstruction, NULL, (WindowPtr) -1L);
  429.     } else {
  430.         constructionwin = GetNewWindow(wConstruction, NULL, (WindowPtr) -1L);
  431.     }
  432.     constructbutton = GetNewControl(cConstructButton, constructionwin);
  433.     researchbutton = GetNewControl(cResearchButton, constructionwin);
  434.     SetPort(constructionwin);
  435.     calc_construction_rects();
  436.     run_length_text = TENew(&runlengthrect, &runlengthrect);
  437.     TESetSelect(0, 32767, run_length_text);
  438.     TEDelete(run_length_text);
  439.     /* (should seed with a heuristic "reasonable" value) */
  440.     sprintf(tmpbuf, "%d", currunlength);
  441.     TEInsert(tmpbuf, strlen(tmpbuf), run_length_text);
  442.     /* Switch to a font for the lists. */
  443.     TextFont(monaco);
  444.     TextSize(9);
  445.     /* Set up the list of all constructing units. */
  446.     tmprect = unitlistrect;
  447.     tmprect.right -= sbarwid;
  448.     SetRect(&listrect, 0, 0, 1, 0);
  449.     SetPt(&cellsize, 300, 12);
  450.     /* Create the list of units itself. */
  451.     construction_unit_list =
  452.         LNew(&tmprect, &listrect, cellsize, 128, constructionwin,
  453.              FALSE, FALSE, FALSE, TRUE);
  454.     /* Now set up the list of types. */
  455.     tmprect = typelistrect;
  456.     tmprect.right -= sbarwid;
  457.     SetRect(&listrect, 0, 0, 1, 0);
  458.     /* (should calc this from the desired font) */
  459.     SetPt(&cellsize, 300, 12);
  460.     constructiontypelist =
  461.         LNew(&tmprect, &listrect, cellsize, 128, constructionwin,
  462.              FALSE, FALSE, FALSE, TRUE);
  463.     init_construction_lists();
  464.     if (1) {
  465.         GDHandle main_screen = GetMainDevice();
  466.         int main_screen_width, main_screen_height;
  467.         Rect tmprect = constructionwin->portRect;
  468.         Rect gdrect = (*main_screen)->gdRect;
  469.  
  470.         main_screen_width = gdrect.right - gdrect.left;
  471.         main_screen_height = gdrect.bottom - gdrect.top;
  472.         MoveWindow(constructionwin,
  473.                    4,
  474.                    main_screen_height - (tmprect.bottom - tmprect.top) - 3,
  475.                    FALSE);
  476.     }
  477.     ShowWindow(constructionwin);
  478. }
  479.  
  480. void
  481. init_construction_lists()
  482. {
  483.     int u;
  484.     Unit *unit;
  485.     Cell tmpcell;
  486.  
  487.     /* Update the list of units. */
  488.     LDoDraw(0, construction_unit_list);
  489.     LDelRow(0, 0, construction_unit_list);
  490.     SetPt(&tmpcell, 0, 0);
  491.     /* Create the vector of constructing units, at a reasonable initial size. */
  492.     if (possibleunits == NULL) {
  493.         possibleunits = make_unit_vector(max(50, numunits));
  494.     }
  495.     clear_unit_vector(possibleunits);
  496.     for_all_side_units(dside, unit) {
  497.         maybe_add_unit_to_construction_list(unit);
  498.     }
  499.     LDoDraw(1, construction_unit_list);
  500.     /* Update the list of types. */
  501.     LDoDraw(0, constructiontypelist);
  502.     LDelRow(0, 0, constructiontypelist);
  503.     SetPt(&tmpcell, 0, 0);
  504.     if (possibletypes == NULL)
  505.       possibletypes = (int *) xmalloc(numutypes * sizeof(int));
  506.     numposstypes = 0;
  507.     for_all_unit_types(u) {
  508.         if (1 /* could be built by some unit that could be on the side */) {
  509.             LAddRow(1, tmpcell.v, constructiontypelist);
  510.             constructible_desc(spbuf, dside, u, NULL);
  511.             LSetCell(spbuf, strlen(spbuf), tmpcell, constructiontypelist);
  512.             ++tmpcell.v;
  513.             possibletypes[numposstypes++] = u;
  514.         }
  515.     }
  516.     LDoDraw(1, constructiontypelist);
  517.     adjust_construction_controls();
  518. }
  519.  
  520. void
  521. reinit_construction_lists()
  522. {
  523.     init_construction_lists();
  524. }
  525.  
  526. /* Draw the construction window by updating the lists and framing them. */
  527.  
  528. void
  529. draw_construction()
  530. {
  531.     Rect tmprect;
  532.  
  533.     calc_construction_rects();
  534.     TEUpdate(&(constructionwin->portRect), run_length_text);
  535.     tmprect = runlengthrect;
  536.     InsetRect(&tmprect, -1, -1);
  537.     FrameRect(&tmprect);
  538.     LUpdate(constructionwin->visRgn, construction_unit_list);
  539.     tmprect = unitlistrect;
  540.     InsetRect(&tmprect, -1, -1);
  541.     FrameRect(&tmprect);
  542. #if 0
  543.     for_all_unit_types(u) {
  544.         if (1 /* could be built by some unit that could be on the side */) {
  545.             constructible_desc(spbuf, dside, u, possibleunits);
  546.             SetPt(&tmpcell, 0, u);
  547.             LSetCell(spbuf, strlen(spbuf), tmpcell, constructiontypelist);
  548.         }
  549.     }
  550. #endif
  551.     LUpdate(constructionwin->visRgn, constructiontypelist);
  552.     tmprect = typelistrect;
  553.     InsetRect(&tmprect, -1, -1);
  554.     FrameRect(&tmprect);
  555.     /* Maybe show the construct button as the default. */
  556.     draw_construction_default();
  557. }
  558.  
  559. /* Draw a heavy outline around the construction button. */
  560.  
  561. void
  562. draw_construction_default()
  563. {
  564.     Rect tmprect;
  565.     GrafPtr oldport;
  566.  
  567.     GetPort(&oldport);
  568.     SetPort(constructionwin);
  569.     tmprect = (*constructbutton)->contrlRect;
  570.     PenSize(3, 3);
  571.     InsetRect(&tmprect, -4, -4);
  572.     if ((*constructbutton)->contrlHilite != 0) {
  573.         PenMode(patBic);
  574.     }
  575.     FrameRoundRect(&tmprect, 16, 16);
  576.     PenNormal();
  577.     SetPort(oldport);
  578. }
  579.  
  580. /* Figure out how to subdivide the construction window for the two lists. */
  581.  
  582. void
  583. calc_construction_rects()
  584. {
  585.     int wid, hgt, divide;
  586.     Rect tmprect;
  587.  
  588.     tmprect = constructionwin->portRect;
  589.     runlengthrect = tmprect;
  590.     runlengthrect.left = runlengthrect.right - 100;  runlengthrect.top = 5;
  591.     runlengthrect.right -= 20;  runlengthrect.bottom = 25;
  592.     wid = tmprect.right - tmprect.left - sbarwid;
  593.     hgt = tmprect.bottom - tmprect.top - sbarwid;
  594.     if (wid / 2 > 220 /* maxtypewid */) {
  595.         divide = wid - 220;
  596.     } else {
  597.         divide = wid / 2;
  598.     }
  599.     SetRect(&unitlistrect, 0, constructtop, divide, hgt);
  600.     InsetRect(&unitlistrect, constructmargin, constructmargin);
  601.     SetRect(&typelistrect, divide, constructtop, wid, hgt);
  602.     InsetRect(&typelistrect, constructmargin, constructmargin);
  603. }
  604.  
  605. void
  606. activate_construction(int activate)
  607. {
  608.     if (activate)
  609.       TEActivate(run_length_text);
  610.     else
  611.       TEDeactivate(run_length_text);
  612.     LActivate(activate, construction_unit_list);
  613.     LActivate(activate, constructiontypelist);
  614. }
  615.  
  616. Unit *
  617. get_selected_construction_unit()
  618. {
  619.     Point tmpcell;
  620.     Unit *unit;
  621.  
  622.     SetPt(&tmpcell, 0, 0);
  623.     if (LGetSelect(TRUE, &tmpcell, construction_unit_list)) {                
  624.         if (tmpcell.v < possibleunits->numunits) {
  625.             unit = (possibleunits->units)[tmpcell.v].unit;
  626.             if (is_acting(unit))
  627.               return unit;
  628.         }
  629.     }
  630.     return NULL;
  631. }
  632.  
  633. int
  634. get_selected_construction_type()
  635. {
  636.     Point tmpcell;
  637.  
  638.     SetPt(&tmpcell, 0, 0);
  639.     if (LGetSelect(TRUE, &tmpcell, constructiontypelist)) {                
  640.         if (tmpcell.v < numposstypes) {
  641.             return possibletypes[tmpcell.v];
  642.         }
  643.     }
  644.     return NONUTYPE;
  645. }
  646.  
  647. void
  648. scroll_to_selected_construction_unit()
  649. {
  650.     Unit *unit;
  651.  
  652.     /* Beep and return if there are no maps open currently. */
  653.     if (maplist == NULL) {
  654.         beep();
  655.         return;
  656.     }
  657.     unit = get_selected_construction_unit();
  658.     if (unit != NULL && inside_area(unit->x, unit->y))
  659.       scroll_best_map_to_unit(unit);
  660. }
  661.  
  662. /* Handle a click anywhere within the construction window. */
  663.  
  664. void
  665. do_mouse_down_construction(Point mouse, int mods)
  666. {
  667.     ControlHandle control;
  668.     short part;
  669.     int u;
  670.     Unit *unit;
  671.     extern int modal_construction;
  672.     extern WindowPtr window_behind_construction;
  673.  
  674.     part = FindControl(mouse, constructionwin, &control);
  675.     if (control == constructbutton) {
  676.         if ((unit = get_selected_construction_unit()) != NULL) {
  677.             if ((u = get_selected_construction_type()) != NONUTYPE) {
  678.                 push_build_task(unit, u, currunlength);
  679.                 execute_task(unit);
  680.                 update_construction_unit_list(unit);
  681.                 if (modal_construction && window_behind_construction != nil)
  682.                   SelectWindow(window_behind_construction);
  683.                 window_behind_construction = NULL;
  684.                 modal_construction = FALSE;
  685.                 return;
  686.             }
  687.         }
  688.     } else if (control == researchbutton) {
  689.         if ((unit = get_selected_construction_unit()) != NULL) {
  690.             if ((u = get_selected_construction_type()) != NONUTYPE) {
  691.                 push_research_task(unit, u, u_tech_to_build(u));
  692.                 execute_task(unit);
  693.                 update_construction_unit_list(unit);
  694.                 if (modal_construction && window_behind_construction != nil)
  695.                   SelectWindow(window_behind_construction);
  696.                 window_behind_construction = NULL;
  697.                 modal_construction = FALSE;
  698.                 return;
  699.             }
  700.         }
  701.     } else if (PtInRect(mouse, &runlengthrect)) {
  702.         TEClick(mouse, mods, run_length_text);
  703.         /* (what else to do?) */
  704.     } else if (PtInRect(mouse, &unitlistrect)) {
  705.         LClick(mouse, mods, construction_unit_list);
  706.         /* Update the type list to show what could be built and in how long. */
  707.         update_type_list_for_unit(get_selected_construction_unit());
  708.     } else if (PtInRect(mouse, &typelistrect)) {
  709.         LClick(mouse, mods, constructiontypelist);
  710.         /* Update the unit list to show what could build the type */
  711.         update_unit_list_for_type(get_selected_construction_type());
  712.     } else {
  713.         /* Click was not in any useful part of the window. */ 
  714.     }
  715. }
  716.  
  717. int
  718. do_key_down_construction(key)
  719. int key;
  720. {
  721.     int len;
  722.     char buffer[10];
  723.     CharsHandle text;
  724.  
  725.     if (1 /* textedit active */) {
  726.         TEKey(key, run_length_text);
  727.         text = TEGetText(run_length_text);
  728.         len = max((*run_length_text)->teLength, 9);
  729.         strncpy(buffer, *text, len);
  730.         buffer[len] = '\0';
  731.         currunlength = atoi(buffer);
  732.         return TRUE;
  733.     }
  734.     return FALSE;
  735. }
  736.  
  737. /* Highlight exactly one specific unit in the construction window, and unhighlight
  738.    any others. */
  739.  
  740. void
  741. select_unit_in_construction_window(Unit *unit)
  742. {
  743.     int i;
  744.     Point tmpcell;
  745.  
  746.     for (i = 0; i < possibleunits->numunits; ++i) {
  747.         SetPt(&tmpcell, 0, i);
  748.         LSetSelect((unit == (possibleunits->units)[i].unit), tmpcell, construction_unit_list);
  749.         LAutoScroll(construction_unit_list);
  750.     }
  751.     update_type_list_for_unit(get_selected_construction_unit());
  752. }
  753.  
  754. void
  755. select_type_in_construction_window(int u)
  756. {
  757.     int i;
  758.     Point tmpcell;
  759.  
  760.     for (i = 0; i < numutypes; ++i) {
  761.         SetPt(&tmpcell, 0, i);
  762.         LSetSelect((u == i), tmpcell, constructiontypelist);
  763.         LAutoScroll(constructiontypelist);
  764.     }
  765.     if (u == NONUTYPE)
  766.       return;
  767.     update_unit_list_for_type(get_selected_construction_type());
  768. }
  769.  
  770. /* Given a unit (which may be any unit), update the list of constructing units. */
  771.  
  772. void
  773. update_construction_unit_list(Unit *unit)
  774. {
  775.     int i, u;
  776.     Point tmpcell;
  777.  
  778.     if (constructionwin == nil)
  779.       return;
  780.     u = get_selected_construction_type();
  781.     /* We need to look for it even if it might not be ours, since it might
  782.        have been captured or otherwise lost, and needs to be removed. */
  783.     for (i = 0; i < possibleunits->numunits; ++i) {
  784.         if (unit == (possibleunits->units)[i].unit) {
  785.             SetPt(&tmpcell, 0, i);
  786.             if (is_active(unit)
  787.                 && can_build(unit)
  788.                 && side_controls_unit(dside, unit)) {
  789.                 construction_desc(spbuf, unit, u);
  790.                 LSetCell(spbuf, strlen(spbuf), tmpcell, construction_unit_list);
  791.             } else {
  792.                 remove_unit_from_vector(possibleunits, unit, i);
  793.                 LDelRow(1, tmpcell.v, construction_unit_list);
  794.             }
  795.             return;
  796.         }
  797.     }
  798.     /* Unit was not found, try to add it to the list. */
  799.     maybe_add_unit_to_construction_list(unit);
  800. }
  801.  
  802. void
  803. maybe_add_unit_to_construction_list(Unit *unit)
  804. {
  805.     Point tmpcell;
  806.  
  807.     if (is_acting(unit)
  808.         && can_build(unit)
  809.         && side_controls_unit(dside, unit)) {
  810.         /* Add this unit to the vector of constructing units. */
  811.         possibleunits = add_unit_to_vector(possibleunits, unit, 0);
  812.         /* (should sort and maybe rearrange list here) */
  813.         /* Add a row at the end of the list. */
  814.         SetPt(&tmpcell, 0, possibleunits->numunits - 1);
  815.         LAddRow(1, possibleunits->numunits - 1, construction_unit_list);
  816.         construction_desc(spbuf, unit, get_selected_construction_type());
  817.         LSetCell(spbuf, strlen(spbuf), tmpcell, construction_unit_list);
  818.     }
  819. }
  820.  
  821. void
  822. update_unit_list_for_type(int u)
  823. {
  824.     int i;
  825.     Point tmpcell;
  826.     Unit *unit;
  827.  
  828.     for (i = 0; i < possibleunits->numunits; ++i) {
  829.         unit = (possibleunits->units)[i].unit;
  830.         if (unit != NULL) {
  831.             SetPt(&tmpcell, 0, i);
  832.             if (is_acting(unit) && unit->side == dside) {
  833.                 construction_desc(spbuf, unit, u);
  834.                 LSetCell(spbuf, strlen(spbuf), tmpcell, construction_unit_list);
  835.             } else {
  836. /*                LDelRow(1, tmpcell.v, construction_unit_list); */
  837.                 LSetCell("", 0, tmpcell, construction_unit_list);
  838.             }
  839.         }
  840.     }
  841.     adjust_construction_controls();
  842. }
  843.  
  844. void
  845. update_construction_type_list()
  846. {
  847.     int u;
  848.  
  849.     if (constructionwin == nil)
  850.       return;
  851.     u = get_selected_construction_type();
  852.     update_type_list_for_unit(get_selected_construction_unit());
  853. }
  854.  
  855. void
  856. update_type_list_for_unit(Unit *unit)
  857. {
  858.     int u;
  859.     Point tmpcell;
  860.  
  861.     for_all_unit_types(u) {
  862.         if (1 /* could be built by some unit that could be on the side */) {
  863.             SetPt(&tmpcell, 0, u);
  864.             constructible_desc(spbuf, dside, u, get_selected_construction_unit());
  865.             LSetCell(spbuf, strlen(spbuf), tmpcell, constructiontypelist);
  866.         }
  867.     }
  868.     adjust_construction_controls();
  869. }
  870.  
  871. /* Enable/disable controls according to whether the selected list elements can
  872.    do construction activities. */
  873.  
  874. void
  875. adjust_construction_controls()
  876. {
  877.     int u, canconstruct = FALSE, canresearch = FALSE;
  878.     Unit *unit;
  879.  
  880.     unit = get_selected_construction_unit();
  881.     if (unit != NULL) {
  882.         u = get_selected_construction_type();
  883.         if (u != NONUTYPE) {
  884.             if (uu_acp_to_create(unit->type, u) > 0)
  885.               canconstruct = TRUE;
  886.             if (uu_acp_to_research(unit->type, u) > 0)
  887.               canresearch = TRUE;
  888.         }
  889.     }
  890.     HiliteControl(constructbutton, (canconstruct ? 0 : 255));
  891.     HiliteControl(researchbutton, (canresearch ? 0 : 255));
  892.     draw_construction_default();
  893. }
  894.  
  895. /* Resize the construction window to the given size. */
  896.  
  897. static void adjust_construction_items(void);
  898.  
  899. void
  900. grow_construction(int h, int v)
  901. {
  902.     EraseRect(&constructionwin->portRect);
  903.     SizeWindow(constructionwin, h, v, 1);
  904.     adjust_construction_items();
  905.     /* This will force a full redraw at the next update. */
  906.     InvalRect(&constructionwin->portRect);
  907. }                    
  908.  
  909. /* Zooming "rightsizes" the window. */
  910.  
  911. void
  912. zoom_construction(int part)
  913. {
  914.     int titleh, vislinesavail;
  915.     Rect zoomrect;
  916.     GDHandle zoomgd;
  917.  
  918.     EraseRect(&constructionwin->portRect);
  919.     if (part == inZoomOut) {
  920.         if (!hasColorQD) {
  921.             /* If no Color QD, then there is only one screen. */
  922.             zoomrect = QD(screenBits).bounds;
  923.         } else {
  924.             zoomgd = best_zoom_screen(&constructionwin->portRect);
  925.             zoomrect = (*zoomgd)->gdRect;
  926.             if (zoomgd == GetMainDevice()) {
  927.                 zoomrect.top += GetMBarHeight();
  928.             }
  929.             titleh = 20; /* (should calc) */
  930.             zoomrect.top += titleh;
  931.         }
  932.         InsetRect(&zoomrect, 4, 4);
  933.         /* If not many units or types, shrink the zoomed window to fit. */
  934.         vislinesavail = (zoomrect.bottom - zoomrect.top - sbarwid) / 15;
  935.         if (0 /*vislinesavail > total_history_lines*/) {
  936.             zoomrect.bottom = zoomrect.top + 20 /*total_history_lines*/ * 15 + sbarwid;
  937.         }
  938.         (*((WStateDataHandle) ((WindowPeek) constructionwin)->dataHandle))->stdState = zoomrect;
  939.     }
  940.     ZoomWindow(constructionwin, part, (constructionwin == FrontWindow()));
  941.     adjust_construction_items();
  942.     /* This will force a full redraw at the next update. */
  943.     InvalRect(&constructionwin->portRect);
  944. }
  945.  
  946. /* Move and resize the list and text objects in the construction window. */
  947.  
  948. static void
  949. adjust_construction_items()
  950. {
  951.     /* Recalculate size and position. */
  952.     calc_construction_rects();
  953.     /* Resize the run length text item. */
  954.     (*run_length_text)->viewRect = runlengthrect;
  955.     (*run_length_text)->destRect = runlengthrect;
  956.     TECalText(run_length_text);
  957.     /* Resize the unit list. */
  958.     LSize(unitlistrect.right - unitlistrect.left - sbarwid,
  959.           unitlistrect.bottom - unitlistrect.top,
  960.           construction_unit_list);
  961.     /* Move the type list (is this the approved way to do it?) */
  962.     (*constructiontypelist)->rView.left = typelistrect.left;
  963.     LSize(typelistrect.right - typelistrect.left - sbarwid,
  964.           typelistrect.bottom - typelistrect.top,
  965.           constructiontypelist);
  966. }
  967.  
  968. /* The side renaming dialog includes places for all the different name-related
  969.    properties of a side. */
  970.  
  971. void
  972. side_rename_dialog(Side *side)
  973. {
  974.     short done = FALSE, changed = TRUE, ditem;
  975.     Str255 tmpstr;
  976.     DialogPtr win;
  977.     short itemtype;  Handle itemhandle;  Rect itemrect;
  978.  
  979.     win = GetNewDialog(dSideRename, NULL, (DialogPtr) -1L);
  980.     while (!done) {
  981.         if (changed) {
  982.             /* Seed the items with the current side names. */
  983.             GetDItem(win, diSideRenameName, &itemtype, &itemhandle, &itemrect);
  984.             c2p((side->name ? side->name : ""), tmpstr);
  985.             SetIText(itemhandle, tmpstr);
  986.             GetDItem(win, diSideRenameFullName, &itemtype, &itemhandle, &itemrect);
  987.             c2p((side->longname ? side->longname : ""), tmpstr);
  988.             SetIText(itemhandle, tmpstr);
  989.             GetDItem(win, diSideRenameAcronym, &itemtype, &itemhandle, &itemrect);
  990.             c2p((side->shortname ? side->shortname : ""), tmpstr);
  991.             SetIText(itemhandle, tmpstr);
  992.             GetDItem(win, diSideRenameNoun, &itemtype, &itemhandle, &itemrect);
  993.             c2p((side->noun ? side->noun : ""), tmpstr);
  994.             SetIText(itemhandle, tmpstr);
  995.             GetDItem(win, diSideRenamePluralNoun, &itemtype, &itemhandle, &itemrect);
  996.             c2p((side->pluralnoun ? side->pluralnoun : ""), tmpstr);
  997.             SetIText(itemhandle, tmpstr);
  998.             GetDItem(win, diSideRenameAdjective, &itemtype, &itemhandle, &itemrect);
  999.             c2p((side->adjective ? side->adjective : ""), tmpstr);
  1000.             SetIText(itemhandle, tmpstr);
  1001.             GetDItem(win, diSideRenameEmblemName, &itemtype, &itemhandle, &itemrect);
  1002.             c2p((side->emblemname ? side->emblemname : ""), tmpstr);
  1003.             SetIText(itemhandle, tmpstr);
  1004.             GetDItem(win, diSideRenameColorScheme, &itemtype, &itemhandle, &itemrect);
  1005.             c2p((side->colorscheme ? side->colorscheme : ""), tmpstr);
  1006.             SetIText(itemhandle, tmpstr);
  1007.             ShowWindow(win);
  1008.             changed = FALSE;
  1009.         }
  1010.         draw_default_button(win, diSideRenameOK);
  1011.         SetCursor(&QD(arrow));
  1012.         ModalDialog(NULL, &ditem);
  1013.         switch (ditem) {
  1014.             case diSideRenameOK:
  1015.                 /* Actually change the side's slots. */
  1016.                 GetDItem(win, diSideRenameName, &itemtype, &itemhandle, &itemrect);
  1017.                 set_side_name(dside, dside, get_string_from_item(itemhandle));
  1018.                 GetDItem(win, diSideRenameFullName, &itemtype, &itemhandle, &itemrect);
  1019.                 side->longname = get_string_from_item(itemhandle);
  1020.                 GetDItem(win, diSideRenameAcronym, &itemtype, &itemhandle, &itemrect);
  1021.                 side->shortname = get_string_from_item(itemhandle);
  1022.                 GetDItem(win, diSideRenameNoun, &itemtype, &itemhandle, &itemrect);
  1023.                 side->noun = get_string_from_item(itemhandle);
  1024.                 GetDItem(win, diSideRenamePluralNoun, &itemtype, &itemhandle, &itemrect);
  1025.                 side->pluralnoun = get_string_from_item(itemhandle);
  1026.                 GetDItem(win, diSideRenameAdjective, &itemtype, &itemhandle, &itemrect);
  1027.                 side->adjective = get_string_from_item(itemhandle);
  1028.                 GetDItem(win, diSideRenameEmblemName, &itemtype, &itemhandle, &itemrect);
  1029.                 side->emblemname = get_string_from_item(itemhandle);
  1030.                 GetDItem(win, diSideRenameColorScheme, &itemtype, &itemhandle, &itemrect);
  1031.                 side->colorscheme = get_string_from_item(itemhandle);
  1032.                 /* Tweak the side menu. */
  1033.                 update_side_menu(dside);
  1034.                 /* Force redisplay of everything that might use any side names. */
  1035.                 force_overall_update();
  1036.                 /* Fall into next case. */
  1037.             case diSideRenameCancel:
  1038.                 done = TRUE;
  1039.                 break;
  1040.             case diSideRenameRandom:
  1041.                 side->name = NULL;
  1042.                 side->noun = NULL;
  1043.                 /* always need to clear this cache before renaming... */
  1044.                 side->pluralnoun = NULL;
  1045.                 side->adjective = NULL;
  1046.                 make_up_side_name(side);
  1047.                 init_emblem_images(side);
  1048.                 changed = TRUE;
  1049.                 break;
  1050.         }
  1051.     }
  1052.     DisposDialog(win);
  1053. }
  1054.  
  1055. /* Unit naming/renaming. */
  1056.  
  1057. int
  1058. unit_rename_dialog(Unit *unit)
  1059. {
  1060.     short done = FALSE, ditem;
  1061.     char *newname;
  1062.     char *namer = unit_namer(unit);
  1063.     Str255 tmpstr;
  1064.     DialogPtr win;
  1065.     short itemtype;  Handle itemhandle;  Rect itemrect;
  1066.  
  1067.     if (unit == NULL)
  1068.       return FALSE;
  1069.     win = GetNewDialog(dRename, NULL, (DialogPtr) -1L);
  1070.     /* Seed the text item with the original name. */
  1071.     newname = unit->name;
  1072.     if (newname == NULL)
  1073.       newname = "";
  1074.     GetDItem(win, diRenameName, &itemtype, &itemhandle, &itemrect);
  1075.     c2p(newname, tmpstr);
  1076.     SetIText(itemhandle, tmpstr);
  1077.     /* Gray out the random renaming button if no namers available. */
  1078.     GetDItem(win, diRenameRandom, &itemtype, &itemhandle, &itemrect);
  1079.     HiliteControl((ControlHandle) itemhandle, ((namer != NULL) ? 0 : 255));
  1080.     ShowWindow(win);
  1081.     while (!done) {
  1082.         draw_default_button(win, diRenameOK);
  1083.         SetCursor(&QD(arrow));
  1084.         ModalDialog(NULL, &ditem);
  1085.         switch (ditem) {
  1086.             case diRenameOK:
  1087.                 GetDItem(win, diRenameName, &itemtype, &itemhandle, &itemrect);
  1088.                 set_unit_name(dside, unit, get_string_from_item(itemhandle));
  1089.                 /* Fall into next case. */
  1090.             case diRenameCancel:
  1091.                 done = TRUE;
  1092.                 break;
  1093.             case diRenameRandom:
  1094.                 newname = propose_unit_name(unit);
  1095.                 GetDItem(win, diRenameName, &itemtype, &itemhandle, &itemrect);
  1096.                 c2p(newname, tmpstr);
  1097.                 SetIText(itemhandle, tmpstr);
  1098.                 break;
  1099.         }
  1100.     }
  1101.     DisposDialog(win);
  1102.     return TRUE;
  1103. }
  1104.  
  1105. /* Unit closeups. */
  1106.  
  1107. UnitCloseup *
  1108. find_unit_closeup(Unit *unit)
  1109. {
  1110.     UnitCloseup *unitcloseup;
  1111.  
  1112.     for_all_unit_closeups(unitcloseup) {
  1113.         if (unitcloseup->unit == unit
  1114.             && unitcloseup->window
  1115.             && ((WindowPeek) unitcloseup->window)->visible)
  1116.           return unitcloseup;
  1117.     }
  1118.     return NULL;
  1119. }
  1120.  
  1121. void
  1122. create_unit_closeup(Unit *unit)
  1123. {
  1124.     int u, w, h;
  1125.     WindowPtr win;
  1126.     UnitCloseup *unitcloseup = (UnitCloseup *) xmalloc(sizeof(UnitCloseup));
  1127.  
  1128.     if (!active_display(dside) || unit == NULL) return;
  1129.     DGprintf("Creating a closeup of %s\n", unit_desig(unit));
  1130.     u = unit->type;
  1131.     unitcloseup->unit = unit;
  1132.     if (hasColorQD) {
  1133.         win = GetNewCWindow(wUnitCloseup, nil, (WindowPtr) -1L);
  1134.     } else {
  1135.         win = GetNewWindow(wUnitCloseup, nil, (WindowPtr) -1L);
  1136.     }
  1137.     unitcloseup->window = win;
  1138.     stagger_window(unitcloseup->window, &lastunitcloseuph, &lastunitcloseupv);
  1139.     preferred_closeup_size(u, &w, &h);
  1140.     SizeWindow(win, w, h, 1);
  1141.     /* (should use unit's name if it has one!) */
  1142.     sprintf(spbuf, "%s #%d Closeup", u_type_name(u), unit->id);
  1143.     add_window_menu_item(spbuf, win);
  1144.     unitcloseup->next = unitcloseuplist;
  1145.     unitcloseuplist = unitcloseup;
  1146.     /* We're now ready to show this closeup to the world. */
  1147.     ShowWindow(win);
  1148. }
  1149.  
  1150. static char *nummrows;
  1151.  
  1152. int closeupspacing = 16;
  1153.  
  1154. int closeupwinwid = 200;
  1155.  
  1156. void
  1157. preferred_closeup_size(int u, int *widp, int *hgtp)
  1158. {
  1159.     int wid = closeupwinwid, hgt, m, u2, count;
  1160.  
  1161.     hgt = 4 + 32 + 4 + 3 * closeupspacing;
  1162.     if (nummrows == NULL) {
  1163.         nummrows = xmalloc(numutypes);
  1164.         for_all_unit_types(u2) {
  1165.             nummrows[u2] = count = 0;
  1166.             for_all_material_types(m) {
  1167.                 if (um_storage_x(u2, m) > 0)
  1168.                       ++count;
  1169.               }
  1170.               if (count > 0) {
  1171.                   nummrows[u2] = max(1, count / 2);
  1172.               }
  1173.         }
  1174.     }
  1175.     hgt += nummrows[u] * closeupspacing;
  1176.     if (u_acp(u) > 0) hgt += 5 * closeupspacing;
  1177.     /* (should make some room for tooling state) */
  1178.     *widp = wid;  *hgtp = hgt;
  1179. }
  1180.  
  1181. /* Draw all the fields and displays in a unit closeup. */
  1182.  
  1183. void
  1184. draw_unit_closeup(UnitCloseup *unitcloseup)
  1185. {
  1186.     int u, m, sx = 4, sy, count, sx2, sy2;
  1187.     char tmpbuf[BUFSIZE];
  1188.     Rect tmprect;
  1189.     GrafPtr oldport;
  1190.     WindowPtr win = unitcloseup->window;
  1191.     Unit *unit = unitcloseup->unit;
  1192.  
  1193.     if (!active_display(dside)) return;
  1194.     if (!in_play(unit) || !(side_controls_unit(dside, unit) || endofgame)) {
  1195.         /* If the unit is no longer alive and ours, shut down the window. */
  1196.         remove_window_menu_item(win);
  1197.         destroy_unit_closeup(unitcloseup);
  1198.         HideWindow(win);
  1199.         return;
  1200.     }
  1201.     GetPort(&oldport);
  1202.     SetPort(win);
  1203.     EraseRect(&win->portRect);
  1204.     u = unit->type;
  1205.     /* Draw the unit's image. */
  1206.     SetRect(&tmprect, sx, sx, sx + 32, sx + 32); 
  1207.     EraseRect(&tmprect);
  1208.     draw_unit_image(win, tmprect.left, tmprect.top,
  1209.                     tmprect.right - tmprect.left, tmprect.bottom - tmprect.top,
  1210.                     u, side_number(unit->side), !completed(unit));
  1211.     /* Draw the unit's name. */
  1212.     name_or_number(unit, tmpbuf);
  1213.     MoveTo(44, 4 + closeupspacing);
  1214.     DrawText(tmpbuf, 0, strlen(tmpbuf));
  1215.     if (Debug || DebugG || DebugM) {
  1216.         sprintf(tmpbuf, " %d ", unit->id);
  1217.         MoveTo(closeupwinwid - 30, 15);
  1218.         DrawText(tmpbuf, 0, strlen(tmpbuf));
  1219.     }
  1220.     /* Draw the unit's side and type. */
  1221.     side_and_type_name(tmpbuf, dside, u, unit->side);
  1222.     MoveTo(44, 4 + closeupspacing * 2);
  1223.     DrawText(tmpbuf, 0, strlen(tmpbuf));
  1224.     /* Draw the unit's location. */
  1225.     if (unit->z != 0) {
  1226.         sprintf(tmpbuf, "; alt %d", unit->z);
  1227.     } else {
  1228.         strcpy(tmpbuf, "");
  1229.     }
  1230.     sprintf(spbuf, "at %d,%d%s", unit->x, unit->y, tmpbuf);
  1231.     if (unit->transport != NULL) {
  1232.         sprintf(tmpbuf, "In %s (%s)", short_unit_handle(unit->transport), spbuf);
  1233.     } else {
  1234.         strcpy(tmpbuf, spbuf);
  1235.     }
  1236.     sy = 4 + 32 + 4 + closeupspacing;
  1237.     MoveTo(sx, sy);
  1238.     DrawText(tmpbuf, 0, strlen(tmpbuf));
  1239.     /* Draw the unit's hit points. */
  1240.     hp_desc(tmpbuf, unit, TRUE);
  1241.     sy += closeupspacing;
  1242.     MoveTo(sx, sy);
  1243.     DrawText(tmpbuf, 0, strlen(tmpbuf));
  1244.     /* Draw the unit's current ACP, if applicable. */
  1245.     if (u_acp(u) > 0) {
  1246.         acp_desc(tmpbuf, unit, TRUE);
  1247.         sy += closeupspacing;
  1248.         MoveTo(sx, sy);
  1249.         DrawText(tmpbuf, 0, strlen(tmpbuf));
  1250.     }
  1251.     /* Draw the unit's supplies. */
  1252.     count = 0;
  1253.     if (nummrows[u] > 0)
  1254.       sy += closeupspacing;
  1255.     for_all_material_types(m) {
  1256.       if (um_storage_x(u, m) > 0) {
  1257.         strcpy(tmpbuf, m_type_name(m));
  1258.         sx2 = sx + (count >= nummrows[u] ? closeupwinwid/2 : 0);  sy2 = sy + (count % nummrows[u]) * closeupspacing;
  1259.         MoveTo(sx2, sy2);
  1260.         DrawText(tmpbuf, 0, strlen(tmpbuf));
  1261.         sprintf(tmpbuf, "%d/%d", unit->supply[m], um_storage_x(u, m));
  1262.         MoveTo(sx2 + (closeupwinwid/2 - 4 - 4 - TextWidth(tmpbuf, 0, strlen(tmpbuf))), sy2);
  1263.         DrawText(tmpbuf, 0, strlen(tmpbuf));
  1264.         ++count;
  1265.       }
  1266.     }
  1267.     if (nummrows[u] > 0)
  1268.       sy += (nummrows[u] - 1) * closeupspacing; 
  1269.     /* Draw the unit's plan, if it has one. */
  1270.     if (unit->plan) {
  1271.         Task *task;
  1272.         extern char *plantypenames[];
  1273.         Plan *plan = unit->plan;
  1274.         
  1275.         sprintf(tmpbuf, "Plan: %s", plantypenames[plan->type]);
  1276.         if (plan->waitingfortasks) strcat(tmpbuf, " [wait]");
  1277.         if (plan->delayed) strcat(tmpbuf, " [delay]");
  1278.         if (plan->reserve) strcat(tmpbuf, " [resv]");
  1279.         if (plan->asleep) strcat(tmpbuf, " [aslp]");
  1280.         if (plan->aicontrol) strcat(tmpbuf, " [dlgt]");
  1281.         if (plan->supply_is_low) strcat(tmpbuf, " [low]");
  1282.         if (plan->supply_alarm) strcat(tmpbuf, " [alrm]");
  1283.         sy += closeupspacing;
  1284.         MoveTo(sx, sy);
  1285.         DrawText(tmpbuf, 0, strlen(tmpbuf));
  1286.         if (plan->maingoal) {
  1287.             /* (should use a "goal_desc" routine) */
  1288.             strcpy(tmpbuf, goal_desig(plan->maingoal));
  1289.             sy += closeupspacing;            
  1290.             MoveTo(sx, sy);
  1291.             DrawText(tmpbuf, 0, strlen(tmpbuf));
  1292.         }
  1293.         if (plan->formation) {
  1294.             /* (should use a "goal_desc" routine) */
  1295.             strcpy(tmpbuf, goal_desig(plan->formation));
  1296.             sy += closeupspacing;            
  1297.             MoveTo(sx, sy);
  1298.             DrawText(tmpbuf, 0, strlen(tmpbuf));
  1299.         }
  1300.         if (plan->tasks) {
  1301.             for (task = plan->tasks; task != NULL; task = task->next) {
  1302.                 task_desc(tmpbuf, task);
  1303.                 sy += closeupspacing;            
  1304.                 MoveTo(sx, sy);
  1305.                 DrawText(tmpbuf, 0, strlen(tmpbuf));
  1306.             }
  1307.         }
  1308.     }
  1309.     SetPort(oldport);
  1310. }
  1311.  
  1312.  
  1313. UnitCloseup *
  1314. unit_closeup_from_window(WindowPtr win)
  1315. {
  1316.     UnitCloseup *unitcloseup;
  1317.     
  1318.     for_all_unit_closeups(unitcloseup) {
  1319.         if (unitcloseup->window == win) return unitcloseup;
  1320.     }
  1321.     return NULL;
  1322. }
  1323.  
  1324. int
  1325. do_mouse_down_unit_closeup(UnitCloseup *unitcloseup, Point mouse, int mods)
  1326. {
  1327.     ControlHandle control;
  1328.     short part;
  1329.     WindowPtr window = unitcloseup->window;
  1330.  
  1331.     part = FindControl(mouse, window, &control);
  1332.     if (0 /* some control */) {
  1333.     } else {
  1334.         /* This just forces a redraw of the window - kind of crude. */
  1335.         update_unit_display(dside, unitcloseup->unit, TRUE);
  1336.         return TRUE;
  1337.     }
  1338. }
  1339.  
  1340. void
  1341. destroy_unit_closeup(UnitCloseup *unitcloseup)
  1342. {
  1343.     UnitCloseup *unitcloseup2;
  1344.     
  1345.     if (unitcloseuplist == unitcloseup) {
  1346.         unitcloseuplist = unitcloseup->next;
  1347.     } else {
  1348.         for_all_unit_closeups(unitcloseup2) {
  1349.             if (unitcloseup2->next == unitcloseup) {
  1350.                 unitcloseup2->next = unitcloseup->next;
  1351.             }
  1352.         }
  1353.     }
  1354.     /* (should destroy substructs) */
  1355.     free(unitcloseup);
  1356. }
  1357.  
  1358. /* History window. */
  1359.  
  1360. int maxvishistlines = 200;
  1361.  
  1362. void
  1363. create_history_window()
  1364. {
  1365.     Rect vscrollrect;
  1366.  
  1367.     historywin = GetNewWindow(wHistory, NULL, (WindowPtr) -1L);
  1368.     /* (should calc max based on size of font and height of screen) */
  1369.     histcontents = (HistEvent **) xmalloc(maxvishistlines * sizeof(HistEvent *));
  1370.     vscrollrect = historywin->portRect;
  1371.     vscrollrect.top -= 1;
  1372.     vscrollrect.bottom -= sbarwid - 1;
  1373.     vscrollrect.left = vscrollrect.right - sbarwid;
  1374.     vscrollrect.right += 1;
  1375.     histvscrollbar = NewControl(historywin, &vscrollrect, "\p", TRUE,
  1376.                                  0, 0, 100, scrollBarProc, 0L);
  1377.     firstvisevt = history->next;
  1378.     secondvisevt = firstvisevt->next;
  1379.     update_total_hist_lines();
  1380.     set_history_scrollbar();
  1381. }
  1382.  
  1383. void
  1384. calc_history_layout()
  1385. {
  1386.     update_total_hist_lines();
  1387.     set_history_scrollbar();
  1388. }
  1389.  
  1390. void
  1391. update_total_hist_lines()
  1392. {
  1393.     HistEvent *hevt;
  1394.     
  1395.     total_history_lines = 0;
  1396.     for (hevt = history->next; hevt != history; hevt = hevt->next) {
  1397.         if (side_in_set(dside, hevt->observers)) {
  1398.             if (hevt->startdate != hevt->prev->startdate) ++total_history_lines;
  1399.             ++total_history_lines;
  1400.         }
  1401.     }
  1402. }
  1403.  
  1404. void
  1405. set_history_scrollbar()
  1406. {
  1407.     int hgt, oldmax;
  1408.     HistEvent *nexthevt;
  1409.  
  1410.     hgt = historywin->portRect.bottom - historywin->portRect.top;
  1411.     numvishistlines = (hgt - history_line_spacing - sbarwid) / history_line_spacing;
  1412.     oldmax = GetCtlMax(histvscrollbar);
  1413.     SetCtlMax(histvscrollbar, max(0, total_history_lines - numvishistlines + 1));
  1414.     HiliteControl(histvscrollbar, (numvishistlines < total_history_lines ? 0 : 255));
  1415.     /* If the thumb was at max, move it to the new max. */
  1416.     if (GetCtlValue(histvscrollbar) == oldmax) {
  1417.         SetCtlValue(histvscrollbar, GetCtlMax(histvscrollbar));
  1418.         if (GetCtlValue(histvscrollbar) == 0) {
  1419.             firstvisevt = history->next;
  1420.             secondvisevt = firstvisevt->next;
  1421.         } else {
  1422.             firstvisevt = get_nth_history_line(dside, GetCtlValue(histvscrollbar), &nexthevt);
  1423.             secondvisevt = nexthevt;
  1424.         }
  1425.     }
  1426. }
  1427.  
  1428. void
  1429. draw_history()
  1430. {
  1431.     int i, headdate;
  1432.     HistEvent *hevt;
  1433.     int numchars;
  1434.     char *datestr, hdatebuf[100];
  1435.  
  1436.     if (!active_display(dside) || historywin == nil) return;
  1437.     /* Build up the array of events and dates to draw. */
  1438.     numhistcontents = 0;
  1439.     hevt = firstvisevt;
  1440.     if (hevt == NULL) {
  1441.         histcontents[numhistcontents++] = NULL;
  1442.         hevt = secondvisevt;
  1443.     }
  1444.     for (; hevt != history; hevt = hevt->next) {
  1445.         if (numhistcontents >= numvishistlines) break;
  1446.         if (side_in_set(dside, hevt->observers)) {
  1447.             if (numhistcontents > 0
  1448.                 && histcontents[numhistcontents - 1] != NULL
  1449.                 && hevt->startdate != histcontents[numhistcontents - 1]->startdate) {
  1450.                 histcontents[numhistcontents++] = NULL;
  1451.             }
  1452.             histcontents[numhistcontents++] = hevt;
  1453.         }
  1454.     }
  1455.     /* Draw the header line. */
  1456.     MoveTo(2, 10);
  1457.     headdate = (firstvisevt ? firstvisevt : secondvisevt)->startdate; 
  1458.     /* (should be relative) */
  1459.     datestr = absolute_date_string(headdate);
  1460.     sprintf(hdatebuf, "(%s)", datestr);
  1461.     /* (should clip to drawing only visible chars) */
  1462.     numchars = strlen(hdatebuf);
  1463.     DrawText(hdatebuf, 0, numchars);
  1464.     /* Now draw each event or date. */
  1465.     for (i = 0; i < numhistcontents; ++i) {
  1466.         if (histcontents[i] != NULL) {
  1467.             draw_historical_event(histcontents[i], i);
  1468.         } else {
  1469.             draw_historical_date(histcontents[i+1], i);
  1470.         }
  1471.     }
  1472. }
  1473.  
  1474. void
  1475. draw_historical_event(HistEvent *hevt, int y)
  1476. {
  1477.     int numchars;
  1478.     char hevtbuf[500];
  1479.  
  1480.     if (hevt == NULL) return;
  1481.     MoveTo(20, history_line_spacing * y + history_top_line_height);
  1482.     historical_event_desc(dside, hevt, hevtbuf);
  1483.     /* (should clip to drawing only visible chars) */
  1484.     numchars = strlen(hevtbuf);
  1485.     DrawText(hevtbuf, 0, numchars);
  1486. }
  1487.  
  1488. void
  1489. draw_historical_date(HistEvent *hevt, int y)
  1490. {
  1491.     int numchars;
  1492.     char *datestr, hdatebuf[100];
  1493.  
  1494.     if (hevt == NULL) return;
  1495.     MoveTo(2, history_line_spacing * y + history_top_line_height);
  1496.     /* (should be relative) */
  1497.     datestr = absolute_date_string(hevt->startdate);
  1498.     strcpy(hdatebuf, datestr);
  1499.     /* (should clip to drawing only visible chars) */
  1500.     numchars = strlen(hdatebuf);
  1501.     DrawText(hdatebuf, 0, numchars);
  1502. }
  1503.  
  1504. void
  1505. update_history_window(HistEvent *hevt)
  1506. {
  1507.     HistEvent *prevfirst, *prevsecond;
  1508.  
  1509.     prevfirst = firstvisevt;
  1510.     prevsecond = secondvisevt;
  1511.     SetPort(historywin);
  1512.     calc_history_layout();
  1513.     if (firstvisevt != prevfirst
  1514.         || secondvisevt != prevsecond
  1515.         || numvishistlines > total_history_lines) {
  1516.         force_update(historywin);
  1517.     }
  1518. }
  1519.  
  1520. pascal void
  1521. history_scroll_proc(ControlHandle control, short code)
  1522. {
  1523.     int curvalue, maxvalue, pagesize, jump;
  1524.  
  1525.     curvalue = GetCtlValue(control);
  1526.     maxvalue = GetCtlMax(control);
  1527.     pagesize = numvishistlines;
  1528.     switch (code) {
  1529.         case inPageDown:
  1530.             jump = max(1, pagesize - 2);
  1531.             break;
  1532.         case inDownButton:
  1533.             jump = 1;
  1534.             break;
  1535.         case inPageUp:
  1536.             jump = min(-1, - (pagesize - 2));
  1537.             break;
  1538.         case inUpButton:
  1539.             jump = -1;
  1540.             break;
  1541.         default:
  1542.             jump = 0;
  1543.             break;
  1544.     }
  1545.     curvalue = max(min(curvalue + jump, maxvalue), 0);
  1546.     SetCtlValue(control, curvalue);
  1547. }
  1548.  
  1549. void
  1550. do_mouse_down_history(Point mouse, int mods)
  1551. {
  1552.     HistEvent *prevfirst, *prevsecond, *nexthevt;
  1553.     ControlHandle control;
  1554.     short part, value;
  1555.  
  1556.     part = FindControl(mouse, historywin, &control);
  1557.     if (control == histvscrollbar) {
  1558.         prevfirst = firstvisevt;
  1559.         prevsecond = secondvisevt;
  1560.         switch (part) {
  1561.             case inThumb:
  1562.                 part = TrackControl(control, mouse, NULL);
  1563.                 break;
  1564.             default:
  1565.                 part = TrackControl(control, mouse, (ProcPtr) history_scroll_proc);
  1566.                 break;
  1567.         }
  1568.         value = GetCtlValue(control);
  1569.         firstvisevt = get_nth_history_line(dside, value, &nexthevt);
  1570.         secondvisevt = nexthevt;
  1571.         if (firstvisevt != prevfirst || secondvisevt != prevsecond) {
  1572.             force_update(historywin);
  1573.         }
  1574.     } else {
  1575.         /* anything to do here? */
  1576.     }
  1577. }
  1578.  
  1579. /* Grow/shrink the history window to the given size. */
  1580.  
  1581. void
  1582. grow_history(int h, int v)
  1583. {
  1584.     EraseRect(&historywin->portRect);
  1585.     SizeWindow(historywin, h, v, 1);
  1586.     move_history_scrollbar(h, v);
  1587.     /* This will force a full redraw at the next update. */
  1588.     InvalRect(&historywin->portRect);
  1589. }                    
  1590.  
  1591. /* Zoom the history window to its best maximal size. */
  1592.  
  1593. void
  1594. zoom_history(int part)
  1595. {
  1596.     int titleh, vislinesavail;
  1597.     Rect zoomrect;
  1598.     GDHandle zoomgd;
  1599.  
  1600.     EraseRect(&historywin->portRect);
  1601.     if (part == inZoomOut) {
  1602.         if (!hasColorQD) {
  1603.             /* If no Color QD, then there is only one screen. */
  1604.             zoomrect = QD(screenBits).bounds;
  1605.         } else {
  1606.             zoomgd = best_zoom_screen(&historywin->portRect);
  1607.             zoomrect = (*zoomgd)->gdRect;
  1608.             if (zoomgd == GetMainDevice()) {
  1609.                 zoomrect.top += GetMBarHeight();
  1610.             }
  1611.             titleh = 20; /* (should calc) */
  1612.             zoomrect.top += titleh;
  1613.         }
  1614.         InsetRect(&zoomrect, 4, 4);
  1615.         /* If not much history, shrink the zoomed window to fit. */
  1616.         vislinesavail = (zoomrect.bottom - zoomrect.top - sbarwid) / history_line_spacing;
  1617.         update_total_hist_lines();
  1618.         if (vislinesavail > total_history_lines) {
  1619.             zoomrect.bottom = zoomrect.top + total_history_lines * history_line_spacing + sbarwid;
  1620.         }
  1621.         (*((WStateDataHandle) ((WindowPeek) historywin)->dataHandle))->stdState = zoomrect;
  1622.     }
  1623.     ZoomWindow(historywin, part, (historywin == FrontWindow()));
  1624.     move_history_scrollbar(window_width(historywin), window_height(historywin));
  1625.     /* This will force a full redraw at the next update. */
  1626.     InvalRect(&historywin->portRect);
  1627. }
  1628.  
  1629. void
  1630. move_history_scrollbar(int h, int v)
  1631. {
  1632.     MoveControl(histvscrollbar, h - sbarwid, 0);
  1633.     SizeControl(histvscrollbar, sbarwid + 1, v - sbarwid + 1);
  1634.     set_history_scrollbar();
  1635. }
  1636.